﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;

namespace WebFetch
{
    public enum State
    {
        SUCCESS,
        FAIL,
    }
    public static class HtmlParserHelper
    {

        #region static method for html Parser
        public static State getHtmlFile(string urlString, string encodingString, ref string temp)
        {


            TextWriter tw = new StreamWriter(@"D:\\awork\\log.txt", true, Encoding.GetEncoding("UTF-8"));

            try
            {
                StringBuilder sb = new StringBuilder();

                // prepare the web page we will be asking for
                HttpWebRequest request = (HttpWebRequest)
                    WebRequest.Create(urlString);

                // execute the request
                HttpWebResponse response = (HttpWebResponse)
                    request.GetResponse();
                Console.WriteLine("=================");
                Console.WriteLine(urlString);
                Console.WriteLine("=================");

                tw.WriteLine("The HttpHeaders are \n\n\tName\t\tValue{0}", request.Headers);
                tw.WriteLine("The HttpHeaders are \n\n\tName\t\tValue{0}", response.Headers);
                tw.Flush();
                //Console.WriteLine("The HttpHeaders are \n\n\tName\t\tValue{0}", request.Headers);
                //Console.WriteLine("The HttpHeaders are \n\n\tName\t\tValue{0}", response.Headers);
                //Console.WriteLine("---------------------------------------------------");


                Stream resStream = response.GetResponseStream();

                byte[] buf = new byte[8192];

                string tempString = null;
                int count = 0;
                do
                {
                    // fill the buffer with data
                    count = resStream.Read(buf, 0, buf.Length);

                    // make sure we read some data
                    if (count != 0)
                    {

                        // translate from bytes to ASCII text
                        tempString = Encoding.GetEncoding(encodingString).GetString(buf, 0, count);
                        // continue building the string
                        sb.Append(tempString);
                    }
                }
                while (count > 0); // any more data to read?
                temp = sb.ToString();
                tw.Close();
                return State.SUCCESS;
            }
            catch (Exception ex)
            {
                if (ex.ToString().Contains("404"))
                {
                    Console.WriteLine(urlString + "did not exist");
                    tw.WriteLine(urlString + "did not exist");
                }
                tw.Close();
                return State.FAIL;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="temp"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static string getProperty(string temp, int start, string property)
        {
            bool pair = false;
            int end = moveToThisTagEnd(temp, start);
            string tempstring = temp.Substring(start + 1, end - start);
            if (tempstring.Contains(property))
            {
                start = tempstring.IndexOf(property) + property.Length;
                start = moveToEqualMark(tempstring, start);
                end = start + 1;
                end = start = skipBlanks(tempstring, end);
                while (tempstring[end] != ' ' && tempstring[end] != '>' && !(tempstring[end] == '/' && tempstring[end + 1] == '>'))
                    end++;
                return tempstring.Substring(start, end - start).Replace("\"", "").Replace("\'", ""); ;
            }
            return null;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="TagName"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static int getTagContent(string TagName, int start)
        {
            System.Diagnostics.Debug.WriteLine(TagName);
            return 0;
        }

        public static int moveToEqualMark(string temp, int start)
        {
            for (; temp[start] != '='; start++) ;
            return start;
        }

        #region Move to Next Tag
        public static int moveToNextTagStart(string temp, int start)
        {
            for (; temp[start] != '<'; start++) ;
            return start;
        }

        public static int moveToNextTagEnd(string temp, int start)
        {
            if (temp[start] == '<')
                start++;
            for (; temp[start] != '<'; start++) ;
            for (; temp[start] != '>'; start++) ;
            return start;
        }
        #endregion Move to Next Tag

        public static int moveToThisTagEnd(string temp, int start)
        {
            for (; temp[start] != '>'; start++) ;
            return start;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="temp"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static string getTagName(string temp, int start)
        {
            int end = start + 1;
            for (; temp[start] != ' ' && temp[start] != '>'; start++) ;
            return temp.Substring(end + 1, start - end - 1);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="temp"></param>
        /// <param name="start"></param>
        /// <returns></returns>
        public static TagsType getTagType(string temp, int start)
        {
            int end = start;
            for (; temp[end] != '>'; end++)
            {
                if (temp[end] == '/')
                    return TagsType.SingleCloseTags;
            }

            return TagsType.DoubleCloseTags;
        }

        public static int skipBlanks(string temp, int postion)
        {
            for (; temp[postion] == ' '; postion++) ;
            return postion;
        }

        public static string mkdir(string main,URL url)
        {
            string urlpath = url.PathWithOutFileName;
            string[] arrayString = urlpath.Split('/');
            main += url.HostName + "\\";

            foreach (string s in arrayString)
            {
                if (s.Contains("?"))
                    continue;
                main += s + "\\";
                if (Directory.Exists(main) == false)
                {
                    Directory.CreateDirectory(main);
                }
            }
            main += url.FileName;
            if (File.Exists(main) == false)
            {
                File.Create(main);
            }

            return main;

            string path = url.Url;
            //if (path.Contains("http://"))
            //{
                
            //}
            bool isFile = false;
            string dirString = path.Substring(7);
            int start = 0;
            for (int i = 0; i < dirString.Length; i++)
            {
                if (dirString[i] == '/')
                {
                    string dirName = dirString.Substring(start, i - start);
                    main += dirName + "\\";
                    if (Directory.Exists(main) == false)
                    {
                        Directory.CreateDirectory(main);
                    }
                    start = i + 1;
                }
                else if (i == dirString.Length - 1) 
                {
                    string fileName = null;
                    if (dirString[i] == '/')
                    {
                        fileName = dirString.Substring(start, i - start);
                    }
                    else
                    {
                        fileName = dirString.Substring(start, i - start + 1);
                    }
                    
                    main += fileName;
                    if (fileName.Contains('.'))
                    {
                        string[] extention = fileName.Split('.');
                        if (IsKnowFileType(extention[extention.Length - 1]))
                        {
                            if (File.Exists(main) == false)
                            {
                                File.Create(main);
                                isFile = true;
                            }
                        }
                        else if (Directory.Exists(main) == false)
                        {
                            Directory.CreateDirectory(main);
                        }
                    }
                    else
                        if (Directory.Exists(main) == false)
                        {
                            Directory.CreateDirectory(main);
                        }

                    i++;
                }

                //End if
            }

            return main[main.Length-1] == '\\'? main:(isFile?main:main+="\\");
        }

        public static bool IsKnowFileType(string extension)
        {
            Regex rType = new Regex("(.*)\\.(.*)");

            Match rMatch = rType.Match(extension);
            if (rMatch.Success == false)
            {
                return false;
            }
            else
            {
                switch (rMatch.Groups[2].Value)
                {
                    case "bmp":
                    case "dib":
                    case "jpg":
                    case "jpeg":
                    case "jpe":
                    case "jfif":
                    case "gif":
                    case "tif":
                    case "tiff":
                    case "png":
                    case "mp3":
                    case "html":
                    case "htm":
                    case "shtml":
                    case "xml":
                    case "php":
                    case "phpx":
                    case "clsp":
                        return true;
                }
            }
            return false;
        }

        public static string ConcatURL(string path, string fileName)
        {
            if (path.EndsWith("/") && fileName.StartsWith("/"))
            {
                path += fileName.Substring(1);
            }
            else if (path.EndsWith("/") || fileName.StartsWith("/"))
            {
                path += fileName;
            }
            else
                path += "/" + fileName;
            return path;
        }

        public static void ConcatFileName(ref string path, string fileName)
        {
            if (path.EndsWith("\\") && fileName.StartsWith("\\"))
            {
                path += fileName.Substring(1);
            }
            else if (path.EndsWith("\\") || fileName.StartsWith("\\"))
            {
                path += fileName;
            }
            else
                path += "\\" + fileName;
        }

        public static string makeUrlDir(string main, URL url,int hashValue)
        {
            string urlpath = url.PathWithOutFileName;
            string[] arrayString = urlpath.Split('/');
            main += url.HostName + "\\";

            foreach (string s in arrayString)
            {
                /*
                 *     星號 (*)

    垂直線 (|)

    冒號 (:)

    雙引號 (")

    小於 (<)

    大於 (>)

    問號 (?)
                 * */
                if (s.Contains("?")
                    ||s.Contains(":")
                    ||s.Contains("\"")
                    ||s.Contains("<")
                    ||s.Contains(">")
                    ||s.Contains("|")
                    ||s.Contains("*")
                    )
                    continue;
                main += s + "\\";
                if (Directory.Exists(main) == false)
                {
                    Directory.CreateDirectory(main);
                }
            }
            main += url.FileName+"-"+hashValue.ToString();
            if (File.Exists(main) == false)
            {
                FileStream f = File.Create(main);
                f.Close();
            }

            return main;

            string mainCopy = main;
            string path = url.PathWithOutFileName;
            string filePath = url.PathWithFileName;
 
            path = path.Replace('/','\\');
            filePath = filePath.Replace('/', '\\');

            path = url.HostName + "\\" + path;
            filePath = url.HostName + "\\" + filePath;

            ConcatFileName(ref main, path);
            ConcatFileName(ref mainCopy, filePath);
            Console.WriteLine("=====================");
            Console.WriteLine(main);
            Console.WriteLine("======================");
            mainCopy = mainCopy +"-"+ hashValue.ToString();
            if (Directory.Exists(main) == false)
            {
                Directory.CreateDirectory(main);
            }

            if (File.Exists(mainCopy) == false)
            {
                FileStream f = File.Create(mainCopy);
                f.Close();
            }

            return mainCopy;
        }

        public static string GetFileExtension(string fileName)
        {
            string[] fileNameSplit = fileName.Split('.');
            return fileNameSplit[fileNameSplit.Length - 1];
        }

        public static int GetUrlLayer(URL url)
        {
            Stack<string> stack = new Stack<string>();

            string path = url.PathWithOutFileName;

            foreach (string pathName in path.Split('/'))
            {
                if (pathName != "")
                {
                    stack.Push(pathName);
                }
            }
            return stack.Count;
        }
        /// <summary>
        /// Get Domain Name
        /// for example, f("www.host.com")="host.com"
        /// </summary>
        /// <param name="hostName"></param>
        /// <returns></returns>
        public static string GetDomainName(string hostName)
        {
            string[] names = hostName.Split('.');
            string domainNameTemp = "";

            for (int i = 1; i < names.Length - 1; i++)
            {
                domainNameTemp += names[i] + ".";
            }
            domainNameTemp += names[names.Length - 1];
            return domainNameTemp;
        }

        /// <summary>
        /// make f("http://www.url.com/abc/../def")=http://www.url.com/def
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string RelateURLToAbsolute(string url)
        {
            Regex rHost = new Regex("http://([^/?]*){1}/?(.*)");
            Match m = rHost.Match(url);

            if (m.Success)
            {
                string host = m.Groups[1].Value;
                string pathWithFileName = RelatedPathToAbsolute(m.Groups[2].Value);

                return "http://" + host + "/" + pathWithFileName;

            }

            return null;
        }

        public static string RelatedPathToAbsolute(string path)
        {
            string[] dirName = path.Split('/');

            Stack<string> validatedPath = new Stack<string>();
            if (dirName.Length <= 1)
                return path;

            foreach (string name in dirName)
            {
                if (name == "..")
                {
                    if (validatedPath.Count > 0) //Bug1 "too many ..", when stack is empty, the error is occured.
                        validatedPath.Pop();
                    continue;
                }

                if (name == ".")
                    continue;
                validatedPath.Push(name);
            }
            string filterPath = "";

            string[] paths = validatedPath.ToArray();
            Array.Reverse(paths);

            foreach (string name in paths)
            {
                filterPath += "/" + name;
            }

            return filterPath.Substring(1);
        }

        #endregion
    }
}
